#include "comunicacao_cliente.h"

int executa_sistema_cliente(int sockfd, struct sockaddr *to){
  request req;
  int opt, i;
#ifdef MARCAR_TEMPOS_CLIENTE
  char nome[200];
  char c = 'a';
  char indice[2];
  FILE *fp; /* Arquivo onde sao escritos os tempos */

  indice[1] = '\0';
  while(1){
    strcpy(nome, ARQ_TEMPO_CLIENTE);
    indice[0] = c;
    c++;
    strcat(nome, indice);
    strcat(nome, EXT);
    if((fp = fopen(nome, "r")))
      fclose(fp);
    else
      break;
  }

  /* Abre o arquivo para escrita */
  fp = fopen(nome, "w");
#endif

  /* Inicializa contadores de bytes */
  bytes_total = 0;
  bytes_corrompidos = 0;

  /* Le dados (apenas para conferir o que foi recebido!) */
  for(i=0; i<idAtual; i++){
    bd_filmes[i] = carregar_informacoes_filme(i);
    bd_filmes[i].titulo[TAM_MAX_TITULO-1] = '\0';
    bd_filmes[i].horario[TAM_MAX_HORARIO-1] = '\0';
    bd_filmes[i].sinopse[TAM_MAX_SINOPSE-1] = '\0';
  }

  printf("\nBem vindo ao sistema de informacoes de filmes!\n");
  while(1){
    do{
      printf("\nEscolha uma das opcoes abaixo: \n\n");
      printf("  1. Listar todos os numeros de filmes com seus titulos\n");
      printf("  2. Mostrar sinopse de um filme\n");
      printf("  3. Mostrar todas as informacoes de um filme\n");
      printf("  4. Listar todas as informacoes de todos os filmes\n");
      printf("  5. Dar nota para um filme\n");
      printf("  6. Mostrar a nota e o numero de clientes de um filme\n");
      printf("  0. Sair\n");
      printf("\n  Opcao: ");
      scanf("%d", &opt);
    }while(opt<0 || opt>6);

    req.opt = '0'+opt;
    req.id = -1;
    req.nota = -1;

    switch(req.opt){
    case '0':  /***************SAIU***************/
#ifdef MARCAR_TEMPOS_CLIENTE
      if(fp)
	fclose(fp);
#endif
      printf("Total de bytes: %d\n", bytes_total);
      printf("Numero de bytes corrompidos: %d\n", bytes_corrompidos);
      printf("Taxa de erros: %.2f% \n", (float)(100*bytes_corrompidos)/(float)bytes_total);
      return 0;
    case '1':  /***************LISTOU TITULOS***************/
#ifdef MARCAR_TEMPOS_CLIENTE
      /* Marca instante anterior a requisicao */
      gettimeofday(&t0_cliente, NULL);
#endif
      if(write_request(sockfd, &req, REQ_OPCAO, to, sizeof(*to))<0)
	return -1;
      if(receber_titulos(sockfd)<0)
	return -1;
      break;
    case '2':  /***************BUSCOU SINOPSE***************/
      printf("Numero do filme: ");
      scanf("%d", &req.id);
      /* Envia numero do filme */
#ifdef MARCAR_TEMPOS_CLIENTE
      /* Marca instante anterior a requisicao descontando entrada */
      gettimeofday(&t0_cliente, NULL);
#endif
      if(write_request(sockfd, &req, REQ_OPCAO_ID, to, sizeof(*to))<0)
	return -1;
      if(receber_sinopse_filme(sockfd, req.id)<0)
	return -1;
      break;
    case '3':  /***************BUSCOU TUDO DE UM FILME***************/
      printf("Numero do filme: ");
      scanf("%d", &req.id);
#ifdef MARCAR_TEMPOS_CLIENTE
      /* Marca instante anterior a requisicao descontando entrada */
      gettimeofday(&t0_cliente, NULL);
#endif
      if(write_request(sockfd, &req, REQ_OPCAO_ID, to, sizeof(*to))<0)
	return -1;
      if(receber_informacoes_filme(sockfd, req.id)<0)
	return -1;
      break;
    case '4':  /***************LISTOU TUDO***************/
#ifdef MARCAR_TEMPOS_CLIENTE
      /* Marca instante anterior a requisicao */
      gettimeofday(&t0_cliente, NULL);
#endif
      if(write_request(sockfd, &req, REQ_OPCAO, to, sizeof(*to))<0)
	return -1;
      if(receber_tudo(sockfd)<0)
	return -1;
      break;
    case '5':  /***************DEU NOTA***************/
      printf("Numero do filme: ");
      scanf("%d", &req.id);
      do{
	printf("Nota (0-100): ");
	scanf("%d", &req.nota);
      }while(req.nota<0 || req.nota>100);
#ifdef MARCAR_TEMPOS_CLIENTE
      /* Marca instante anterior a requisicao */
      gettimeofday(&t0_cliente, NULL);
#endif
      if(write_request(sockfd, &req, REQ_OPCAO_ID_NOTA, to, sizeof(*to))<0){
	printf("Erro no envio da nota.\n");
	return -1;
      }
      if(receber_confirmacao_nota(sockfd, req.id, req.nota)<0)
	return -1;
      break;
    case '6':  /***************LEU NOTA***************/
      printf("Numero do filme: ");
      scanf("%d", &req.id);
#ifdef MARCAR_TEMPOS_CLIENTE
      /* Marca instante anterior a requisicao descontando entrada */
      gettimeofday(&t0_cliente, NULL);
#endif
      if(write_request(sockfd, &req, REQ_OPCAO_ID, to, sizeof(*to))<0)
	return -1;
      if(receber_nota_filme(sockfd, req.id)<0)
	return -1;
      break;
    }

#ifdef MARCAR_TEMPOS_CLIENTE
    /* Salva o tempo medido em um arquivo */
    fprintf(fp, "%lf;\n", t1_cliente.tv_sec-t0_cliente.tv_sec+(t1_cliente.tv_usec-t0_cliente.tv_usec)/(double)1000000);
#endif

  }

  return 0;
}

int receber_titulos(int sockfd){
  int i, num_filmes, num_bytes, num_timeouts = 0, len;
  char titulo[MAX_FILMES][TAM_MAX_TITULO];

  bzero(titulo, TAM_MAX_TITULO*MAX_FILMES*sizeof(char));

  /* Recebe os titulos */
  if(read_int(sockfd, &num_filmes, MSG_DONTWAIT, NULL, NULL)<0)
    return -1;
  if(num_filmes>MAX_FILMES)
    num_filmes = MAX_FILMES;
  for(i=0; i<num_filmes; i++){
    if((num_bytes=read_string(sockfd, titulo[i], TAM_MAX_TITULO, MSG_DONTWAIT, NULL, NULL))<0)
      return -1;
    else if(num_bytes==0)
      num_timeouts++;

    /* Testa se o maximo de timeouts foi atingido */
    if(num_timeouts>MAX_TIMEOUTS)
      break;
  }
#ifdef MARCAR_TEMPOS_CLIENTE
  /* Marca o tempo no instante apos o recebimento */
  gettimeofday(&t1_cliente, NULL);
#endif

  /* Imprime saida */
  printf("\nListagem de todos os titulos cadastrados:\n\n");
  bytes_total += sizeof(int);
  bytes_corrompidos += buffer_diff(&idAtual, &num_filmes, sizeof(int));
  for(i=0; i<num_filmes; i++){
    len = (strlen(bd_filmes[i].titulo)+1)*sizeof(char);
    bytes_total += len;
    bytes_corrompidos += buffer_diff(bd_filmes[i].titulo, titulo[i], len);
    printf("%3.1d. ", i);
    fputs(titulo[i], stdout);
    printf("\n");
  }

  printf("\n");
  return 0;
}

int receber_sinopse_filme(int sockfd, int idfilme){
  char sinopse[TAM_MAX_SINOPSE];
  int len;
  if(read_string(sockfd, sinopse, TAM_MAX_SINOPSE, MSG_DONTWAIT, NULL, NULL)<0)
    return -1;
#ifdef MARCAR_TEMPOS_CLIENTE
  /* Marca o tempo no instante apos o recebimento */
  gettimeofday(&t1_cliente, NULL);
#endif
  printf("\nSinopse do filme numero %d: \n\n", idfilme);
  len = (strlen(bd_filmes[idfilme].sinopse)+1)*sizeof(char);
  bytes_total += len;
  bytes_corrompidos += buffer_diff(bd_filmes[idfilme].sinopse, sinopse, len);
  fputs(sinopse, stdout);
  printf("\n\n");
  return 0;
}

int receber_informacoes_filme(int sockfd, int idfilme){
  char titulo[TAM_MAX_TITULO];
  char horario[TAM_MAX_HORARIO];
  int sala, len;
  char sinopse[TAM_MAX_SINOPSE];
  int nota, numClientes;
  if(read_string(sockfd, titulo, TAM_MAX_TITULO, MSG_DONTWAIT, NULL, NULL)<0)
    return -1;
  if(read_string(sockfd, horario, TAM_MAX_HORARIO, MSG_DONTWAIT, NULL, NULL)<0)
    return -1;
  if(read_int(sockfd, &sala, MSG_DONTWAIT, NULL, NULL)<0)
    return -1;
  if(read_string(sockfd, sinopse, TAM_MAX_SINOPSE, MSG_DONTWAIT, NULL, NULL)<0)
    return -1;
  if(read_int(sockfd, &nota, MSG_DONTWAIT, NULL, NULL)<0)
    return -1;
  if(read_int(sockfd, &numClientes, MSG_DONTWAIT, NULL, NULL)<0)
    return -1;
  
#ifdef MARCAR_TEMPOS_CLIENTE
  /* Marca o tempo no instante apos o recebimento */
  gettimeofday(&t1_cliente, NULL);
#endif

  /* Checagem de bytes */
  len = (strlen(bd_filmes[idfilme].titulo)+1)*sizeof(char);
  bytes_total += len;
  bytes_corrompidos += buffer_diff(bd_filmes[idfilme].titulo, titulo, len);

  len = (strlen(bd_filmes[idfilme].horario)+1)*sizeof(char);
  bytes_total += len;
  bytes_corrompidos += buffer_diff(bd_filmes[idfilme].horario, horario, len);

  bytes_total += sizeof(int);
  bytes_corrompidos += buffer_diff(&bd_filmes[idfilme].sala, &sala, sizeof(int));

  len = (strlen(bd_filmes[idfilme].sinopse)+1)*sizeof(char);
  bytes_total += len;
  bytes_corrompidos += buffer_diff(bd_filmes[idfilme].sinopse, sinopse, len);

  bytes_total += sizeof(int);
  bytes_corrompidos += buffer_diff(&bd_filmes[idfilme].nota, &nota, sizeof(int));

  bytes_total += sizeof(int);
  bytes_corrompidos += buffer_diff(&bd_filmes[idfilme].numClientes, &numClientes, sizeof(int));

  /* Imprime as informacoes */
  printf("\nInformacoes do filme numero %d: \n\n", idfilme);
  printf("Titulo: ");
  fputs(titulo, stdout);
  printf("\nHorarios: ");
  fputs(horario, stdout);
  printf("\nSala: %d", sala);
  printf("\nSinopse: ");
  fputs(sinopse, stdout);
  printf("\nNota media do filme %d: %d\n", idfilme, nota);
  printf("Numero de clientes que deram a nota: %d\n", numClientes);
  printf("\n-----------------------------------------------\n");
  return 0;
}

int receber_tudo(int sockfd){
  int i, num_filmes, num_bytes, num_timeouts = 0, len;
  tFilme filme[MAX_FILMES];

  bzero(filme, MAX_FILMES*sizeof(tFilme));

  /* Recebe todas as informacoes */
  if(read_int(sockfd, &num_filmes, MSG_DONTWAIT, NULL, NULL)<0)
    return -1;
  if(num_filmes>MAX_FILMES)
    num_filmes = MAX_FILMES;
  for(i=0; i<num_filmes; i++){

    /* Checa erros nos bytes */
    if((num_bytes=read_string(sockfd, filme[i].titulo, TAM_MAX_TITULO, MSG_DONTWAIT, NULL, NULL))<0)
      return -1;
    else if(num_bytes==0)
      num_timeouts++;

    if((num_bytes=read_string(sockfd, filme[i].horario, TAM_MAX_HORARIO, MSG_DONTWAIT, NULL, NULL))<0)
      return -1;
    else if(num_bytes==0)
      num_timeouts++;

    if((num_bytes=read_int(sockfd, &filme[i].sala, MSG_DONTWAIT, NULL, NULL))<0)
      return -1;
    else if(num_bytes==0)
      num_timeouts++;

    if((num_bytes=read_string(sockfd, filme[i].sinopse, TAM_MAX_SINOPSE, MSG_DONTWAIT, NULL, NULL))<0)
      return -1;
    else if(num_bytes==0)
      num_timeouts++;

    if((num_bytes=read_int(sockfd, &filme[i].nota, MSG_DONTWAIT, NULL, NULL))<0)
      return -1;
    else if(num_bytes==0)
      num_timeouts++;

    if((num_bytes=read_int(sockfd, &filme[i].numClientes, MSG_DONTWAIT, NULL, NULL))<0)
      return -1;
    else if(num_bytes==0)
      num_timeouts++;

    /* Testa se o numero de timeouts passou do maximo */
    if(num_timeouts > MAX_TIMEOUTS)
      break;
  }

#ifdef MARCAR_TEMPOS_CLIENTE
  /* Marca o tempo no instante apos o recebimento */
  gettimeofday(&t1_cliente, NULL);
#endif

  bytes_total += sizeof(int);
  bytes_corrompidos += buffer_diff(&idAtual, &num_filmes, sizeof(int));
  /* Imprime a saida */
  printf("\nInformacoes de todos os filmes cadastrados:\n\n");
  for(i=0; i<num_filmes; i++){

    /* Checagem de bytes */
    len = (strlen(bd_filmes[i].titulo)+1)*sizeof(char);
    bytes_total += len;
    bytes_corrompidos += buffer_diff(bd_filmes[i].titulo, filme[i].titulo, len);
    len = (strlen(bd_filmes[i].horario)+1)*sizeof(char);
    bytes_total += len;
    bytes_corrompidos += buffer_diff(bd_filmes[i].horario, filme[i].horario, len);
    bytes_total += sizeof(int);
    bytes_corrompidos += buffer_diff(&bd_filmes[i].sala, &filme[i].sala, sizeof(int));
    len = (strlen(bd_filmes[i].sinopse)+1)*sizeof(char);
    bytes_total += len;
    bytes_corrompidos += buffer_diff(bd_filmes[i].sinopse, filme[i].sinopse, len);

    printf("\nInformacoes do filme numero %d: \n\n", i);
    printf("Titulo: ");
    fputs(filme[i].titulo, stdout);
    printf("\nHorarios: ");
    fputs(filme[i].horario, stdout);
    printf("\nSala: %d", filme[i].sala);
    printf("\nSinopse: ");
    fputs(filme[i].sinopse, stdout);
    printf("\nNota media do filme %d: %d\n", i, filme[i].nota);
    printf("Numero de clientes que deram a nota: %d\n", filme[i].numClientes);
    printf("\n-----------------------------------------------\n");
  }
  return 0;
}

int receber_confirmacao_nota(int sockfd, int idfilme, int nota){
  int id;
  if(read_int(sockfd, &id, MSG_DONTWAIT, NULL, NULL)<0)
    return -1;

#ifdef MARCAR_TEMPOS_CLIENTE
  /* Marca o tempo no instante apos o recebimento */
  gettimeofday(&t1_cliente, NULL);
#endif

  /* Atualiza nota para comparar retorno do servidor */
  bd_filmes[idfilme].nota = (bd_filmes[idfilme].nota*bd_filmes[idfilme].numClientes+nota)/(++bd_filmes[idfilme].numClientes);
  salvar_informacoes_filme(&bd_filmes[idfilme]);

  bytes_total += sizeof(int);
  bytes_corrompidos += buffer_diff(&idfilme, &id, sizeof(int));

  printf("Nota enviada para o filme %d.\n", id);

  return 0;
}

int receber_nota_filme(int sockfd, int idfilme){
  int nota_media, num_clientes;
  if(read_int(sockfd, &nota_media, MSG_DONTWAIT, NULL, NULL)<0)
    return -1;
  if(read_int(sockfd, &num_clientes, MSG_DONTWAIT, NULL, NULL)<0)
    return -1;

#ifdef MARCAR_TEMPOS_CLIENTE
  /* Marca o tempo no instante apos o recebimento */
  gettimeofday(&t1_cliente, NULL);
#endif

  bytes_total += sizeof(int);
  bytes_corrompidos += buffer_diff(&nota_media, &bd_filmes[idfilme].nota, sizeof(int));
  bytes_total += sizeof(int);
  bytes_corrompidos += buffer_diff(&num_clientes, &bd_filmes[idfilme].numClientes, sizeof(int));

  printf("\nNota media do filme %d: %d\n", idfilme, nota_media);
  printf("Numero de clientes que deram a nota: %d\n", num_clientes);

  return 0;
}

int buffer_diff(void *buff1, void *buff2, int len){
  int i, diff = 0;
  for(i=0; i<len; i++){
    if(*(char*)buff1!=*(char*)buff2)
      diff++;
    buff1++;
    buff2++;
  }
  return diff;
}
